//
//  MachExceptionHandler.m
//  crashreport
//
//  Created by william on 12-8-8.
//  Copyright (c) 2012年 __MyCompanyName__. All rights reserved.
//

#import "MachExceptionHandler.h"
#import <dlfcn.h>
#import <mach/machine/thread_status.h>	
#import <mach/thread_status.h>
#import <mach/mach.h>
#import <mach/exception.h>
#import <mach/task.h>
#import <mach/mach_error.h>
#import <pthread.h>

typedef boolean_t (*ExcSrv)(mach_msg_header_t *request,
                             mach_msg_header_t *reply);

#define DEFAULT_MSG_SIZE 2048

static ExcSrv EXC_SERVER;



@implementation MachExceptionHandler
DEFINE_SINGLETON(MachExceptionHandler);

-(void) watcherThread
{
    mach_port_t task;
    mach_port_t exceptionPort;
    
    EXC_SERVER = (ExcSrv)dlsym(RTLD_DEFAULT, "exc_server");
    NSAssert(EXC_SERVER != NULL, @"Can't find exc_server");
    
    task = mach_task_self();
    
    MachSysCall(mach_port_allocate(task,
                                   MACH_PORT_RIGHT_RECEIVE,
                                   &exceptionPort));
    
    MachSysCall(mach_port_insert_right(task,exceptionPort,exceptionPort,
                                       MACH_MSG_TYPE_MAKE_SEND));
    
    thread_state_flavor_t flavor;
    
#ifdef __i386__
    flavor = x86_THREAD_STATE32;
#elif __arm__
    flavor = ARM_THREAD_STATE;
#else
#error Unsupported architecture
#endif
    
    
    
    MachSysCall(task_set_exception_ports(task, 
                                         EXC_MASK_BAD_INSTRUCTION
                                         |EXC_MASK_BAD_ACCESS
                                         |EXC_MASK_ARITHMETIC
                                         , exceptionPort, EXCEPTION_STATE_IDENTITY,  flavor));
    
    CRSay(@"Crash safty net activated!");
    [m_event signal];
    
    MachSysCall( mach_msg_server(EXC_SERVER, DEFAULT_MSG_SIZE,
                    exceptionPort,
                    MACH_MSG_TIMEOUT_NONE));
}

-(void) initialize
{
    
    [NSThread detachNewThreadSelector:@selector(watcherThread) toTarget:self withObject:nil];
    
    [m_event wait];
    
    CRSay(@"Initialized!");
}

-(id) init
{
    if(self = [super init])
    {
        INSTANCE_VAR_FOR(MachExceptionHandler) = self;
        m_event = [[AutoEvent alloc] init];
    }
    
    return self;
}

-(void) dealloc
{
    [m_event release], m_event = nil;
    [super dealloc];
}

@end
